package ikelib;
// CRC calculation class
// 2011.5 T.Ikeda

public class Crc {
	private long [] Table;
	private long Register;
	private static final int  ByteLength=8;
	private static int TableSize = 0;
	private long GenPoly;
	private int GenPolyOrder=16;
	private long MaskBitTable=0;
	private int  MaskBitByte=0;
	private boolean ShiftLeft=true;
	//
	// 生成多項式  x16+x12+x3+x+1 (ACT) = 1 0001 0000 0000 1011 = 0x1100b 
	//           x16+x12+x5+1 (CCITT) = 1 0001 0000 0010 0001  0x11021 -> poly = 0x1021
	// checked
	//  CRC-CCITT,Initial=0,FinalXor=0,Shit Left ,Byte Order=MSByte first,Bit Order MSBit first
	//    input "1" -> CRC = 9842(0x2672)
	// ShiftLeft : ture 左送り　false 右送り
	public Crc(long poly,boolean shiftleft){
		
		ShiftLeft = shiftleft;
		
		MaskBitTable = 0L;
		for(int i=0;i<GenPolyOrder;i++){
			MaskBitTable |= (long)(1<<i);
		}
		MaskBitByte = 0;
		for(int i=0;i<ByteLength;i++){
			MaskBitByte |= (1<<i);
		}
		
		TableSize = (int)Math.pow(2.0,ByteLength);
		// generate table
		GenPoly=poly;
		Table = new long[TableSize];
		if(ShiftLeft){
			for(int i=0;i<TableSize;i++){
				Register = (long)(i<<(GenPolyOrder-ByteLength));
				for(int j=0;j<ByteLength;j++){
					boolean topbit= (Register & (1<<(GenPolyOrder-1)))!=0;
					Register <<=1;
					if(topbit){
						Register ^= GenPoly; 
					}
				}
				Table[i]=(long)(MaskBitTable & Register);
			}
		}
		else{
			for(int i=0;i<TableSize;i++){
				Register = (long)i;
				for(int j=0;j<ByteLength;j++){
					boolean topbit= (Register & 1)!=0;
					Register >>=1;
					if(topbit){
						Register ^= GenPoly; 
					}
				}
				Table[i]=(long)(MaskBitTable & Register);
			}
		}
	}
	private void putByte(int mes){
		if(ShiftLeft){
			long top = (Register>>(GenPolyOrder-ByteLength));
			top ^= (long)(mes & MaskBitByte);
			Register = (Register<<ByteLength) ^ Table[(int)(top & MaskBitByte)];
		}
		else{
			long top = Register;
			top ^= (long)(mes & MaskBitByte);
			Register = (Register>>ByteLength) ^ Table[(int)(top & MaskBitByte)];
		}
	}
	public long calc(byte[] mes,int length){
		Register = 0;
		for(int i=0;i<length;i++){
			int dat;
			if(mes[i]<0) dat = 0x100+mes[i];
			else	  dat = mes[i];
			putByte(dat);
		}
		return Register;
	}
}
